package com.jt.utils.log;

import com.alibaba.fastjson.JSON;
import com.jt.enums.SysLogEnum;
import com.jt.mapper.SysLogMapper;
import com.jt.pojo.SysLog;
import com.jt.utils.JwtUtils;
import com.mysql.cj.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

@Aspect
@Component
@Slf4j
public class SysAop {

    @Autowired
    private SysLogMapper sysLogMapper;

    @Pointcut("@annotation(com.jt.utils.log.SysLogAnnotation)")
    public void pointCat() {
    }

    @Pointcut("execution(* com.jt.controller..*.*(..))")
    public void operExceptionLogPoinCut() {
    }

    @AfterReturning(value = "pointCat()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        Map<String, Object> map = new HashMap<>();
        SysLog sysLog = new SysLog();
        try {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            /*获取方法名*/
            String methodName = method.getName();
            /*获取类型*/
            String className = joinPoint.getTarget().getClass().getName();
            methodName = className + "." + methodName;
            map.put("UUID", UUID.randomUUID().toString().substring(8));
            sysLog.setRequestMethod(methodName);
            /*获取用户信息*/
            String token = request.getHeader("Authorization");
            if (!StringUtils.isNullOrEmpty(token)) {
                Map<String, Object> stringObjectMap = JwtUtils.JieXi(token);
                sysLog.setUsername(stringObjectMap.get("username").toString());
            }
            /*获取操作，给注解设值*/
            SysLogAnnotation annotation = method.getAnnotation(SysLogAnnotation.class);
            if (!Objects.isNull(annotation)) {
                String operDesc = annotation.operDesc();
                String operModul = annotation.operModul();
                SysLogEnum sysLogEnum = annotation.operType();
                map.put("描述信息=>", operDesc);
                sysLog.setOperDesc(operDesc);
                sysLog.setTitle(operModul);
                sysLog.setBusinessType(sysLogEnum.getCode());
                map.put("模块=>", operModul);
                map.put("类型=>", sysLogEnum);
            }
            map.put(" 请求方法名=>", methodName);
            sysLog.setMethodName(methodName);
            /*获取请求参数*/
            Map<String, String[]> parameterMap = request.getParameterMap();
            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                map.put(" 请求cookies", Arrays.stream(cookies));
            }
            Map<String, String> mapRes = zhuanHuan(parameterMap);
            String params = JSON.toJSONString(mapRes);
            map.put(" 请求参数", params);
            map.put(" 请求结果", JSON.toJSONString(keys));
            map.put(" 请求URI", request.getRequestURI());
            sysLog.setOperUrl(request.getRequestURI());
            sysLogMapper.insert(sysLog);
            log.info("打印操作日志{}", map.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @AfterThrowing(value = "operExceptionLogPoinCut()", throwing = "e")
    public void saveThrowLog(JoinPoint joinPoint, Throwable e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        Map<String, Object> map = new HashMap<>();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            map.put("uuid", UUID.randomUUID().toString().replace("-", ""));
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            // 请求的参数
            Map<String, String> map1 = zhuanHuan(request.getParameterMap());
            // 将参数所在的数组转换成json
            String params = JSON.toJSONString(map1);
            map.put("params", params);// 请求参数
            map.put("methodName", methodName);// 请求方法名
            map.put("excName", e.getClass().getName());
            map.put("excMessage", stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));// 异常信息
            map.put("requestURI", request.getRequestURI());// 操作URI
            map.put("excCreateTime", new Date());// 发生异常时间
            /**
             * 可以进行入库,只用写个Mapper文件,进行个插入即可;本处只进行打印
             */
            log.error("打印异常日志:{}", map.toString());
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }

    private Map<String, String> zhuanHuan(Map<String, String[]> parameterMap) {
        Map<String, String> resMap = new HashMap<String, String>();
        Set<String> stringSet = parameterMap.keySet();
        for (String key : stringSet) {
            resMap.put(key, parameterMap.get(key)[0]);
        }
        return resMap;
    }

    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }

}


